﻿

using System.Runtime.CompilerServices;

namespace PhraseCorrector.SpellCore
{
    /**
     * 辅助类，用于计算编辑距离和相似度。
     */
    internal static class Helpers
    {

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 确定当一个或两个字符串为 null 时编辑距离函数的正确返回值。
        /// </summary>
        /// <param name="string1">第一个字符串。</param>
        /// <param name="string2">第二个字符串。</param>
        /// <param name="maxDistance">最大距离。</param>
        /// <returns>如果两个字符串都为 null，则返回 0；如果只有一个字符串为 null，则返回另一个字符串的长度（如果小于等于最大距离），否则返回 -1。</returns>
        public static int NullDistanceResults(string string1, string string2, double maxDistance)
        {
            return (string1== null)
                 ? String1Null(string2, maxDistance)
                : DistanceAndString(string1, maxDistance);
        }
  
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////      
        /// <summary>
        /// 检查字符串是否为null或长度是否小于等于最大距离。
        /// </summary>
        /// <param name="string2">要检查的字符串。</param>
        /// <param name="maxDistance">最大距离阈值。</param>
        /// <returns>如果字符串为null，则返回0；如果字符串长度小于等于最大距离，则返回长度；否则返回-1。</returns>
        public static int String1Null(string string2, double maxDistance)
        {
            return (string2== null) ? 0 : (string2.Length <= maxDistance) ? string2.Length : -1;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 检查字符串长度是否小于等于最大距离。
        /// </summary>
        /// <param name="string1">要检查的字符串。</param>
        /// <param name="maxDistance">最大距离阈值。</param>
        /// <returns>如果字符串长度小于等于最大距离，则返回长度；否则返回-1。</returns>
        public static int DistanceAndString(string string1, double maxDistance)
        {
            return (string1.Length <= maxDistance) ? string1.Length : -1;
        }
        
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 确定当一个或两个字符串为 null 时相似度函数的正确返回值。
        /// </summary>
        /// <param name="string1">第一个字符串。</param>
        /// <param name="string2">第二个字符串。</param>
        /// <param name="minSimilarity">最小相似度。</param>
        /// <returns>如果两个字符串都为 null，则返回 1；如果其中一个字符串为 null，则返回 0（如果最小相似度大于等于 0），否则返回 -1。</returns>
        public static int NullSimilarityResults(string string1, string string2, double minSimilarity)
        {
            return (string1== null && string2== null) ? 1 : (0 <= minSimilarity) ? 0 : -1;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 计算两个字符串的起始位置和长度，排除公共前缀和后缀。
        /// </summary>
        /// <remarks>假设 string1.Length 小于或等于 string2.Length。</remarks>
        /// <param name="string1">第一个字符串。</param>
        /// <param name="string2">第二个字符串。</param>
        /// <param name="len1">第一个字符串的长度（排除公共前缀和后缀后的长度）。</param>
        /// <param name="len2">第二个字符串的长度（排除公共前缀和后缀后的长度）。</param>
        /// <param name="start">起始位置。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void PrefixSuffixPrep(string string1, string string2, out int len1, out int len2, out int start)
        {
            len2= string2.Length;
            len1= string1.Length; // 这也是两个字符串中最短的长度
            // 排除两个字符串共有的前缀
            PrefixSuffixPrep_0(string1, string2, ref len1, ref len2, out start);
        }
        public static void PrefixSuffixPrep_0(string string1, string string2, ref int len1, ref int len2, out int start) {
            // 排除两个字符串共有的后缀
            PrefixSuffixPrep1(string1, string2, ref len1, ref len2);
            // 排除两个字符串共有的前缀
            PrefixSuffixPrep0(string1, string2, ref len1, ref len2, out start);
        }
        public static void PrefixSuffixPrep0(string string1, string string2, ref int len1, ref int len2, out int start) {
            // 排除两个字符串共有的前缀
            start= PrefixSuffixPrep(string1, string2, len1, 0);
            // 排除公共前缀和后缀后的长度
            PrefixSuffixPrep2(start, ref len1, ref len2);
        }

        public static void PrefixSuffixPrep1(string string1, string string2, ref int len1, ref int len2) {
            while (len1 != 0 && string1[len1 - 1]== string2[len2 - 1]) {
                len1--;
                len2--;
            }
        }
        public static int PrefixSuffixPrep(string string1, string string2, int len1, int start) {
            // 排除两个字符串共有的前缀
            while (start != len1 && string1[start]== string2[start])
                start++;
            return start;
        }
        public static void PrefixSuffixPrep2( int start, ref int len1,ref int len2) {
            if (start != 0) {
                len2 -= start; 
                len1 -= start;
            }
        }
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 从编辑距离计算相似度。
        /// </summary>
        /// <param name="distance">两个字符串之间的编辑距离。</param>
        /// <param name="length">较长字符串的长度。</param>
        /// <returns>相似度值，范围从 0 到 1.0（1 - (distance / length)）。</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static double ToSimilarity(this int distance, int length)
        {
            return (distance < 0) ? -1 : 1 - (distance / (double)length);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 从相似度计算编辑距离。
        /// </summary>
        /// <param name="similarity">两个字符串之间的相似度。</param>
        /// <param name="length">较长字符串的长度。</param>
        /// <returns>编辑距离，范围从 0 到 length（length * (1 - similarity)）。</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int ToDistance(this double similarity, int length)
        {
            return (int)((length * (1 - similarity)) + 0.0000000001);
        }
    }
}

